package com.msite.service.activiti;

import com.msite.util.Constant;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.ParticipantProcess;
import org.activiti.engine.impl.pvm.process.TransitionImpl;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.image.impl.DefaultProcessDiagramCanvas;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by tpeng on 2016/4/25.
 */
@Component
public class ActivitiService {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private ProcessEngine processEngine;

    /**
     * 根据流程定义key获取流程定义
     * @param key
     * @return
     */
    public ProcessDefinition getProcessDefinitionByKey(String key){
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
            .processDefinitionKey(key)
            .latestVersion().active().singleResult();
        return definition;
    }

    /**
     * 根据流程定义key，业务key
     * 获取该流程活动节点相关数据。
     * @param processDefinitionKey
     * @param bussinessKey
     * @return {minX=int, minY=int, actInfos=list}
     * @throws Exception
     */
    public Map<String, Object> getProcessTrace(String processDefinitionKey, String bussinessKey) {
        Execution execution = runtimeService.createExecutionQuery()
                .processDefinitionKey(processDefinitionKey)
                .processInstanceBusinessKey(bussinessKey)
                .singleResult();
        String currExecActId = execution.getActivityId();

        List<Map<String, Object>> activityInfos = new ArrayList<Map<String, Object>>();
        ProcessDefinition definition = getProcessDefinitionByKey(processDefinitionKey);
        //获取部署的流程定义Entity
        ProcessDefinitionEntity processDefinition =
                (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
                        .getDeployedProcessDefinition(definition.getId());
        //获得当前任务的所有节点
        List<ActivityImpl> activitiList = processDefinition.getActivities();
        /*InputStream xmlIs = repositoryService.getResourceAsStream(
                processDefinition.getDeploymentId(), processDefinition.getResourceName());
        BpmnModel bm = new BpmnXMLConverter()
                .convertToBpmnModel(new InputStreamSource(xmlIs), false, true);*/
        BpmnModel bpmnModel = repositoryService.getBpmnModel(definition.getId());
//        long a = System.currentTimeMillis();
        int[] minXY = getMinxMinY_(processDefinition);
//        System.out.println("1->"+(System.currentTimeMillis() - a)+"ms");
//        a = System.currentTimeMillis();
        //int[] minXY2 = getMinXMinY2_(bpmnModel);
        //System.out.println("2->"+(System.currentTimeMillis() - a)+"ms");
        //遍历当前任务节点
        for (ActivityImpl activity : activitiList) {
            Map<String, Object> activityInfo = new HashMap<String, Object>();
            activityInfo.put("isCurrAct", activity.getId().equals(currExecActId));
            activityInfo.put("width", activity.getWidth());
            activityInfo.put("height", activity.getHeight());
            activityInfo.put("x", activity.getX() - minXY[0] + 5); //源码裁剪图时 -5 这里加上
            activityInfo.put("y", activity.getY() - minXY[1] + 5);
            activityInfo.put("actId", activity.getId());
            activityInfo.put("name", activity.getProperty("name"));
            activityInfo.put("actType", Constant.ACTIVIT_TYPES.get(activity.getProperty("type")));
            activityInfo.put("actDocumentation", activity.getProperty("documentation"));
            activityInfo.put("definitionDesc", activity.getProcessDefinition().getDescription());
            activityInfos.add(activityInfo);
        }
        Map<String, Object> resMap = new HashMap<String, Object>();
        resMap.put("minX", minXY[0]);
        resMap.put("minY", minXY[1]);
        resMap.put("actInfos", activityInfos);
        return resMap;
    }

    /**
     * 通过流程定义的节点，流程线节点方式 获取minX,minY {速度快}
     * @param processDefinition
     * @return
     */
    private int[] getMinxMinY_(ProcessDefinitionEntity processDefinition){
        int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE;
        if(processDefinition.getParticipantProcess()!=null){
            ParticipantProcess pProc = processDefinition.getParticipantProcess();
            minX = pProc.getX();
            minY = pProc.getY();
        }
        for(ActivityImpl activity : processDefinition.getActivities()){
            if(activity.getX() < minX){ minX = activity.getX(); }
            if(activity.getY() < minY){ minY = activity.getY(); }
            for(PvmTransition sequenceFlow : activity.getOutgoingTransitions()){
                List<Integer> waypoints = ((TransitionImpl)sequenceFlow).getWaypoints();
                for(int i = 0; i < waypoints.size(); i += 2){
                    if(waypoints.get(i) < minX){ minX = waypoints.get(i);}
                    if(waypoints.get(i+1) < minY){ minY = waypoints.get(i+1);}
                }
            }
        }
        minX = minX == Integer.MAX_VALUE ? 0 : minX;
        minY = minY == Integer.MAX_VALUE ? 0 : minY;
        return new int[]{minX, minY };
    }

    /**
     * 采用反射将ProcessDiagramCanvas 的minX,minY数值 {速度260ms附近，不建议使用}
     * @param bpmnModel
     * @return
     */
    @Deprecated
    private int[] getMinXMinY2_(BpmnModel bpmnModel) {
        int minX = 0, minY = 0;
        try {
            DefaultProcessDiagramGenerator processDiagramGenerator =
                    (DefaultProcessDiagramGenerator) processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();

            Method method = processDiagramGenerator.getClass().getDeclaredMethod("initProcessDiagramCanvas",
                    new Class[]{BpmnModel.class, String.class, String.class, String.class, ClassLoader.class});
            method.setAccessible(true);
            DefaultProcessDiagramCanvas processDiagramCanvas = (DefaultProcessDiagramCanvas)
                    method.invoke(processDiagramGenerator, bpmnModel, "", "", "", null);
            Field minXField = processDiagramCanvas.getClass().getDeclaredField("minX");
            Field minYField = processDiagramCanvas.getClass().getDeclaredField("minY");
            minXField.setAccessible(true);
            minYField.setAccessible(true);
            minX = minXField.getInt(processDiagramCanvas);
            minY = minYField.getInt(processDiagramCanvas);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return new int[]{minX, minY };
    }

}
